﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using MyBasicLib.Collections.Generic; 
using MyBasicLib.Utilities;

using MyBasicLib.Linq.Expressions;
using MyBasicLib.Extensions;
namespace MyBasicLib.Data.ORMLiteNew
{

  
    public interface IDbQueryableInfo<T>
    { }
    public class SelectDbQueryableInfo<T> : IDbQueryableInfo<T>
    {
        public bool IsDistinct;
        public bool IsCount;
        public int Skip;
        public int Take;
        public Type SourceType;
        public Type ResultType;
        public Expression Expression;
        public Expression WhereExpression;
        public Queue<DbExpression> JoinExpressions = new Queue<DbExpression>();
        // public Queue<DbExpression> GroupExpressions = new Queue<DbExpression>();
        public Queue<DbExpression> OrderExpressions = new Queue<DbExpression>();
       // public TableAliasCache TableAliasCache = new TableAliasCache();

    }

    public class InsertDbQueryableInfo<T> : IDbQueryableInfo<T>
    {
        public object Entity;
        public SelectDbQueryableInfo<T> SelectDbQueryableInfo;
    }
    public class UpdateDbQueryableInfo<T> : IDbQueryableInfo<T>
    {
        public object Entity;
        public Expression Expression;
        public SelectDbQueryableInfo<T> SelectDbQueryableInfo;

    }
    public class DeleteDbQueryableInfo<T> : IDbQueryableInfo<T>
    {
        public SelectDbQueryableInfo<T> SelectDbQueryableInfo;
    }


    class DbQueryParser
    {
        public static IDbQueryableInfo<TElement> Parse<TElement>(IDbQueryable<TElement> queryable)
        {

            Type sourceType = null;
            Boolean isCount = false, isDistinct = false;
            List<Expression> whereExpression = new List<Expression>();
            int skip = 0, take = 0;
            Expression selectExpression = null;
            Queue<DbExpression> joinExpressions = new Queue<DbExpression>();
            Queue<DbExpression> orderExpressions = new Queue<DbExpression>();
            //Queue<DbExpression> groupExpressions = new Queue<DbExpression>();
            DbExpression insertExpression = null, updateExpression = null, deleteExpression = null;
            var dbExpressions = queryable.DbExpressionList;

            for (int i = 0; i < dbExpressions.Count; i++)
            {
                DbExpression dbExpression = dbExpressions[i];
                switch (dbExpression.DbExpressionType)
                {
                    case DbExpressionType.None:
                        break;
                    case DbExpressionType.GetTable:
                        sourceType = ((ConstantExpression)dbExpression.Expression).Value as Type;
                        break;
                    case DbExpressionType.All:
                        break;
                    case DbExpressionType.Any:
                        break;
                    case DbExpressionType.Average:
                        // info.ComputExpression = dbExpression;
                        break;
                    case DbExpressionType.Count:
                        // info.CountString = " COUNT(*)";
                        isCount = true;
                        if (dbExpression.Expression != null)
                            whereExpression.Add(dbExpression.Expression);
                        break;
                    case DbExpressionType.Distinct:
                        isDistinct = true;
                        break;
                    case DbExpressionType.First:
                    case DbExpressionType.FirstOrDefault:
                        take = 1;
                        if (dbExpression.Expression != null)
                            whereExpression.Add(dbExpression.Expression);
                        break;
                    case DbExpressionType.GroupBy:
                        //groupExpressions.Enqueue(dbExpression);
                        break;
                    case DbExpressionType.GroupJoin:
                    case DbExpressionType.Join:

                        selectExpression = dbExpression.Expressions[3];
                        joinExpressions.Enqueue(dbExpression);
                        break;
                    case DbExpressionType.Max:
                        break;
                    case DbExpressionType.Min:
                        break;
                    case DbExpressionType.OrderBy:
                    case DbExpressionType.OrderByDescending:
                        orderExpressions.Enqueue(dbExpression);
                        break;
                    case DbExpressionType.Select:
                        selectExpression = dbExpression.Expression;
                        break;
                    case DbExpressionType.SelectMany: //todo  
                        selectExpression = dbExpression.Expressions[1];
                        //joinExpressions.Enqueue(dbExpression);
                        break;
                    case DbExpressionType.Single:
                    case DbExpressionType.SingleOrDefault:
                        take = 1;
                        if (dbExpression.Expression != null)

                            whereExpression.Add(dbExpression.Expression);
                        break;
                    case DbExpressionType.Skip:
                        skip = (int)((ConstantExpression)dbExpression.Expression).Value;
                        break;
                    case DbExpressionType.Sum:
                        break;
                    case DbExpressionType.Take:
                        take = (int)((ConstantExpression)dbExpression.Expression).Value;
                        break;
                    case DbExpressionType.ThenBy:
                    case DbExpressionType.ThenByDescending:
                        orderExpressions.Enqueue(dbExpression);
                        break;
                    case DbExpressionType.Where:
                        whereExpression.Add(dbExpression.Expression);

                        break;
                    case DbExpressionType.Insert:
                        insertExpression = dbExpression;
                        break;
                    case DbExpressionType.Delete:
                        deleteExpression = dbExpression;
                        break;
                    case DbExpressionType.Update:
                        updateExpression = dbExpression;

                        break;

                    default:
                        throw new ArgumentOutOfRangeException("dbExpressions");
                }
            }
            if (insertExpression == null && deleteExpression == null && updateExpression == null &&
             selectExpression == null && !isCount) //string.IsNullOrEmpty(info.CountString)
            {
                selectExpression = Expression.Constant(sourceType); 
            }

         
            if (isCount)
            {
                orderExpressions.Clear();
            }
            var sinfo = new SelectDbQueryableInfo<TElement>();
            sinfo.SourceType = sourceType;
            sinfo.Expression = selectExpression;
            //sinfo.GroupExpressions = groupExpressions;
            sinfo.IsCount = isCount;
            sinfo.IsDistinct = isDistinct;
            sinfo.JoinExpressions = joinExpressions;
            sinfo.OrderExpressions = orderExpressions;
            sinfo.Skip = skip;
            sinfo.Take = take;
            sinfo.WhereExpression = MergeWhereExpressions(whereExpression);
            if (selectExpression != null)
            {
                var lmd = (selectExpression as LambdaExpression);
                if (lmd != null)
                    sinfo.ResultType = lmd.Body.Type;

                var cons = (selectExpression as ConstantExpression);
                if (cons != null)
                    sinfo.ResultType = cons.Value as Type;
            }

            
            if (updateExpression != null)
            {
                var uinfo = new UpdateDbQueryableInfo<TElement>();
                var constantExpression =updateExpression.Expression as    ConstantExpression;
                if (constantExpression != null)
                {
                    uinfo.Entity = constantExpression.Value;
                }
                else
                {
                    uinfo.Expression = updateExpression.Expression;
                }
                uinfo.SelectDbQueryableInfo = sinfo; 
                return uinfo;
            }
            if (deleteExpression != null)
            {
                var dinfo = new DeleteDbQueryableInfo<TElement>();
                dinfo.SelectDbQueryableInfo = sinfo;
                return dinfo;
            }

            if (insertExpression != null)
            {
                var iInfo = new InsertDbQueryableInfo<TElement>();
                if (insertExpression.Expression != null)
                    iInfo.Entity = (insertExpression.Expression as ConstantExpression).Value;
                iInfo.SelectDbQueryableInfo = sinfo;
                return iInfo;
            }
            return sinfo;
        }
        static Expression MergeWhereExpressions(IList<Expression> queue)
        {
            if (queue.Count == 0) return null;

            Expression exp = ((LambdaExpression)queue[0].TrimUnary()).Body;
            for (var i = 1; i < queue.Count; i++)
            {
                Expression nExp = queue[i];
                if (nExp == null) continue;
                exp = Expression.And(exp, ((LambdaExpression)nExp.TrimUnary()).Body);
            }
            return exp;
        }
    }
}
